package com.brian.mongo.aop;

import com.alibaba.fastjson.JSONObject;
import com.brian.mongo.annotation.BrianCache;
import com.brian.mongo.domain.Param;
import com.brian.mongo.domain.User;
import com.brian.mongo.service.RedisService;
import com.brian.mongo.utils.EhCacheUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * @program: architect
 * @author: Brian Huang
 * @create: 2019-07-26 07
 **/
@Aspect
@Component
public class BrianCacheAop {

    @Autowired
    private EhCacheUtils ehCacheUtils;

    @Autowired
    private RedisService redisService;

    private String cacheName = "user";

    @Pointcut("execution(public * com.brian.mongo.service.*.*(..))")
    public void pct(){}

    @Around(value = "pct()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        BrianCache brianCache = getAnnotationClass(proceedingJoinPoint);
        if(brianCache == null){
            //没有该注解放行
            return proceedingJoinPoint.proceed();
        }
        // 1.先查询一级缓存 key 以 当前的类名+方法名称+id +参数值
        String className = getClass(proceedingJoinPoint);
        String methodName = getMethod(proceedingJoinPoint);
        String params = getParams(proceedingJoinPoint);

        String key = className + "-" + methodName
                + "-id:" + params;
        //1.走已经缓存ehcache一级缓存，基于本地JVM
        User user  = (User) ehCacheUtils.get(cacheName, key);
        if(user != null) {
             return user;
        }

        //2.本地JVM没有缓存，走redis二级缓存
        String strUser = redisService.getString(key);
        if(!StringUtils.isEmpty(strUser)){
            JSONObject jsonObject = new JSONObject();
            User user1 = jsonObject.parseObject(strUser, User.class);
            //放一份到一级缓存
            ehCacheUtils.put(cacheName,key,user1);
            return user1;
        }

        //3.走db
        User result = (User) proceedingJoinPoint.proceed();

        //存一份到放缓存
        ehCacheUtils.put(cacheName,key,result);
        redisService.setString(key,result.toString());

        return result;
    }


    /**
     * 获取自定义注解类
     * @param proceedingJoinPoint
     * @return
     */
    private BrianCache getAnnotationClass(ProceedingJoinPoint proceedingJoinPoint){
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        if(signature == null){
            return null;
        }
        Method method = signature.getMethod();
        BrianCache declaredAnnotation = method.getDeclaredAnnotation(BrianCache.class);
        return declaredAnnotation;
    }

    /**
     * 获取类名
     * @param proceedingJoinPoint
     * @return
     */
    private String getClass(ProceedingJoinPoint proceedingJoinPoint){

        return proceedingJoinPoint.getTarget().toString();
    }

    /**
     * 获取方法名
     * @param proceedingJoinPoint
     * @return
     */
    private String getMethod(ProceedingJoinPoint proceedingJoinPoint){
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        if(signature == null){
            return null;
        }
        Method method = signature.getMethod();
        String methodName = method.getName();
        return methodName;
    }

    /**
     * 获取参数
     * @param proceedingJoinPoint
     * @return
     */
    private String getParams(ProceedingJoinPoint proceedingJoinPoint){
        Object[] parameters = proceedingJoinPoint.getArgs();
        Param<String> str = new Param<>();
        Arrays.stream(parameters).forEach(p -> {
            str.setValue(p.toString());
        });
        String params = str.getParams();
        return params;
    }
}
